#ifndef _CHARGE_PROTOCOL_H_
#define _CHARGE_PROTOCOL_H_

#include "charge_os.h"

#ifdef __cplusplus
extern "C"{
#endif

#define MAX_CMD_LEN                         1024
#define SEND_BUF_SIZE                       2048
#define RECV_BUF_SIZE                       1024
#define PILE_ID_MAX_LEN                     32
#define OTA_FILE_NAME_LEN                   32
#define OTA_MAX_PACKAGE_LEN                 512
#define OTA_REPEATED_REQUESTS_NUM           5

#pragma pack(1)
typedef struct _cp56time2a {
    uint16_t msec;
    uint8_t min :6;
    uint8_t res1:1;
    uint8_t iv  :1;
    uint8_t hour:5;
    uint8_t res2:2;
    uint8_t su  :1;
    uint8_t mday:5;
    uint8_t wday:3;
    uint8_t mon :4;
    uint8_t res3:4;
    uint8_t year:7;
    uint8_t res4:1;
} cp56time2a;
#pragma pack()

#define ERR_BASE                                    (0x0000)
#define ERR_SUCCESS                                 (ERR_BASE - 0x0000)
#define ERR_PROTOCOL_BASE                           (-0x0100)
#define ERR_PROTOCOL_NULL_POINTER                   (ERR_PROTOCOL_BASE - 0x0001)
#define ERR_PROTOCOL_MAGIC_INVALID                  (ERR_PROTOCOL_BASE - 0x0002)
#define ERR_PROTOCOL_VERSION_INVALID                (ERR_PROTOCOL_BASE - 0x0003)
#define ERR_PROTOCOL_LEN_ERROR                      (ERR_PROTOCOL_BASE - 0x0004)
#define ERR_PROTOCOL_CRC_INVALID                    (ERR_PROTOCOL_BASE - 0x0005)
#define ERR_PROTOCOL_BUFFER_NOT_ENOUGH              (ERR_PROTOCOL_BASE - 0x0006)
#define ERR_PROTOCOL_BUFFER_ERROR                   (ERR_PROTOCOL_BASE - 0x0007)
#define ERR_ACK_OF_UNKNOWN                          (ERR_PROTOCOL_BASE - 0x0008)
#define ERR_ACCESS_DENY                             (ERR_PROTOCOL_BASE - 0x0009)
#define ERR_RESEND_EXCEED_TIMES                     (ERR_PROTOCOL_BASE - 0x000A)
#define ERR_PROTOCOL_CMD_INVALID                    (ERR_PROTOCOL_BASE - 0x000B)
#define ERR_PROTOCOL_OFFLINE                        (ERR_PROTOCOL_BASE - 0x000C)
#define ERR_CARD_FAIL                               (ERR_PROTOCOL_BASE - 0x000D)
#define ERR_NOT_EXPECT_SEQ                          (ERR_PROTOCOL_BASE - 0x000E)

typedef struct {
    uint8_t     pile_id[PILE_ID_MAX_LEN];
    uint8_t     access_status;
    uint8_t     random_str[6];
    uint8_t     sign[32];   
    uint8_t     crypt_key[44];
}cmd_f1_t;

typedef struct {
    uint8_t     pile_id[PILE_ID_MAX_LEN];
}cmd_00_t;

typedef struct {
    uint8_t     pile_id[PILE_ID_MAX_LEN];
    uint8_t     _switch;
}cmd_03_t;

//服务器应答充电结束
typedef struct {
    uint8_t     pile_id[PILE_ID_MAX_LEN];
    uint8_t     gun_id;
    uint8_t     rsp_state;
}cmd_25_t;

//APP 下发停止充电指令
typedef struct {
    uint8_t     pile_id[PILE_ID_MAX_LEN];
    uint8_t     gun_id;
}cmd_28_t;

//云服务器确认账单
typedef struct {
    uint8_t     pile_id[PILE_ID_MAX_LEN];
    uint8_t     gun_id;
    uint8_t     time[7];
    uint8_t     confirm_state;
}cmd_61_t;

typedef struct {
    uint8_t     pile_id[PILE_ID_MAX_LEN];
    uint8_t     gun_id;
    uint8_t     _switch;
} cmd_91_t;

typedef enum 
{
    CMD_F0 = 0xf0,//认证请求
    CMD_F1 = 0xf1,//认证回复
    CMD_F2 = 0xf2,//心跳请求
    CMD_F3 = 0xf3,//心跳回复
    CMD_F4 = 0xf4,//充电桩上报版本信息
    CMD_F5 = 0xf5,//云服务器下发获取版本指令
    CMD_00 = 0x00,//获取电气参数
    CMD_01 = 0x01,//上报电气参数
    CMD_02 = 0x02,//上报环境数据
    CMD_03 = 0x03,//下发重启充电桩指令
    CMD_04 = 0x04,//重启充电桩回复
    CMD_05 = 0x05,//下发功率分配策略指令
    CMD_06 = 0x06,//应答功率分配策略指令
    CMD_07 = 0x07,//设置充电桩工作参数指令
    CMD_08 = 0x08,//应答工作参数设置指令
    CMD_21 = 0x21,//上报充电枪实时数据
    CMD_22 = 0x22,//上报刷卡启动充电
    CMD_23 = 0x23,//刷卡启动充电回复
    CMD_24 = 0x24,//充电枪主动上报结束充电
    CMD_25 = 0x25,//平台应答充电结束
    CMD_26 = 0x26,//APP下发启动充电指令
    CMD_27 = 0x27,//充电枪应答APP启动指令
    CMD_28 = 0x28,//APP下发停止充电指令
    CMD_29 = 0x29,//充电枪应答APP停止指令
    CMD_2A = 0x2A,//充电桩上报VIN码启动充电
    CMD_2B = 0x2B,//云服务器应答VIN码启动
    CMD_2C = 0x2C,//充电枪通过APP账号启动充电指令
    CMD_2D = 0x2D,//云服务器应答APP账号启动指令
    CMD_2E = 0x2E,//充电枪通过APP账号下发停止充电指令
    CMD_2F = 0x2F,//云服务器应答APP账号停止指令
    CMD_30 = 0x30,//平台下发启动充电指令
    CMD_31 = 0x31,//充电枪应答平台启动指令
    CMD_32 = 0x32,//平台下发停止停止充电指令
    CMD_33 = 0x33,//充电枪应答平台停止指令
    CMD_34 = 0x34,//云服务器下发充电枪二维码
    CMD_35 = 0x35,//充电桩查询充电枪二维码
    CMD_36 = 0x36,//云服务器下发充电枪卡枪解除指令
    CMD_37 = 0x37,//充电枪应答卡枪解除指令
    CMD_50 = 0x50,//充电桩上报BMS参数配置阶段数据
    CMD_51 = 0x51,//充电桩上报BMS充电阶段数据
    CMD_52 = 0x52,//充电桩上报充电中止BMS-CST数据
    CMD_53 = 0x53,//充电桩上报充电中止BMS-BST数据
    CMD_54 = 0x54,//充电桩上报BMS充电结束阶段数据
    CMD_60 = 0x60,//充电桩上报BMS充电结束阶段数据
    CMD_61 = 0x61,//云服务器确认账单
    CMD_70 = 0x70,//云服务器下发电费计价策略
    CMD_71 = 0x71,//充电桩查询电费计价策略
    CMD_90 = 0x90,//地锁实时状态数据
    CMD_91 = 0x91,//云服务器下发控制地锁指令
    CMD_92 = 0x92,//云服务器下发控制地锁指令
    CMD_B0 = 0xb0,//云服务器下发同步时间数据
    CMD_B1 = 0xb1,//充电桩发送同步时间请求
    CMD_C0 = 0xc0,//故障数据上传
    CMD_C1 = 0xc1,//故障恢复上传
    CMD_D1 = 0xD1,//云服务器下发升级指令
    CMD_D2 = 0xD2,//充电桩应答升级指令
    CMD_D3 = 0xD3,//充电桩请求下载升级包
    CMD_D4 = 0xD4,//云服务器应答下载请求
    CMD_D5 = 0xD5,//充电桩发送升级下载结果

    CMD_FF = 0xff,//数据格式错误报文
} CMD_t;


#define CMD_05_LEN          37
#define CMD_D1_LEN          70
#define CMD_D2_LEN          33
#define CMD_D3_LEN          38
#define CMD_D4_MIN_LEN      39
#define CMD_D5_LEN          33

typedef enum {
    CMD_TYPE_UP,                //仅上传
    CMD_TYPE_UP_RESP,           //上传后需云端回复
    CMD_TYPE_UP_RESP_OR_DOWN,   //上传后需云端回复，或平台会主动下发
    CMD_TYPE_DOWN,              //仅云端下发
    CMD_TYPE_DOWN_RESP          //云端下发后需设备回复
} CMD_TYPE_T;

typedef int32_t cmd_decode_cb(void *param, uint8_t *data, uint16_t data_len);
typedef int32_t cmd_encode_cb(void *param, uint8_t *data, uint16_t data_len);

typedef struct cmd_map {
    uint8_t                 cmd_send;
    uint8_t                 cmd_recv;
    uint8_t                 cmd_type;
    cmd_encode_cb*          encode_cb;
    cmd_decode_cb*          decode_cb;
} cmd_map_t;

#define PROTO_CMD_MAP_CNT              (sizeof(_proto_cmd_map)/sizeof(cmd_map_t))
#define PROTO_CMD_MAP_CMD_SEND(i)       _proto_cmd_map[i].cmd_send
#define PROTO_CMD_MAP_CMD_RECV(i)       _proto_cmd_map[i].cmd_recv

#define PROTO_MIN_LEN                   9       //magic(2)+version(2)+cmd(1)+length(2)+crc(2)
#define PROTO_MAGIC                     0x18FF
#define PROTO_VERSION                   0x0001
#define PROTO_SEQ_OFFSET                7
#define PROTO_CRC_LEN                   2

#define BYTES_TO_UINT32_LITTLE(data)        ((uint32_t)data[3] << 24) | \
                                            ((uint32_t)data[2] << 16) | \
                                            ((uint32_t)data[1] << 8) | \
                                            ((uint32_t)data[0])

#define BYTES_TO_UINT16_LITTLE(data)        ((uint32_t)data[1] << 8) | \
                                            ((uint32_t)data[0])

typedef struct {
    uint16_t    magic;
    uint16_t   	version;
    uint8_t     cmd;
    uint16_t    length;         //length(seq + ack + data)
    uint16_t   	seq;
    uint16_t   	ack;
    uint16_t    crc;
    uint8_t*    data;           //include region (seq + ack + data)
} charge_protocol;

#define U16_LO(x)  ((uint8_t) ((uint16_t)(x) & 0xFF)) 
#define U16_HI(x)  ((uint8_t) ((uint16_t)(x) >> 8))

typedef enum {
    NET_STATUS_TRY_CONNECT      = 0,
    NET_STATUS_CONNECTED        = 1,
    NET_STATUS_TRY_ACCESS       = 2,
    NET_STATUS_ACCESS_ALLOW     = 3,
    NET_STATUS_DISCONNECT       = 4,
} NET_STATUS_t;

typedef enum {
    PROTO_QUEUED_UNSENT,
    PROTO_QUEUED_AWAITING_ACK,
    PROTO_QUEUED_COMPLETE
} PROTO_Q_ITEM_STATE_T;

//sizeof net_queue_item_t = 20
typedef struct {
    uint8_t                 *start;
    uint32_t                size;
    uint8_t                 state;
    uint8_t                 cmd;
    uint16_t                seq;
    uint32_t                time_sent;
    uint8_t                 send_times;
} net_queue_item_t;

/*
  *
  *   |  data0  |  data1  |   data2  |  ......  | item2  |  item1  |  item0  |
  *
  */
typedef struct {
    void                    *start;
    void                    *end;
    uint8_t                 *curr;
    uint32_t                free_len;
    net_queue_item_t        *item_info;
} net_queue_t;

#define net_queue_get_item(q_ptr, index) (((net_queue_item_t*) ((q_ptr)->end)) - 1 - index)
#define net_queue_item_cnt(q_ptr) (((net_queue_item_t*) ((q_ptr)->end)) - (q_ptr)->item_info)
#define net_queue_free_len(q_ptr) (q_ptr->curr >= (uint8_t*) ((q_ptr)->item_info - 1)) ? 0 : ((uint8_t*) ((q_ptr)->item_info - 1)) - (q_ptr)->curr

typedef struct {
    uint8_t                 *start;
    uint32_t                size;
    uint8_t                 *curr;
    uint32_t                free_len;
} proto_rcv_buf_t;

#define QUENU_MUTEX_TIME        2000

typedef struct net_client
{
    os_socket_t             sock_fd;
    NET_STATUS_t            net_status;
    uint32_t                time_of_last_recv;
    net_queue_t             snd_queue;
    os_mutex_t              snd_data_mutex;

    proto_rcv_buf_t         rcv_buf;
    uint8_t                 sbuf[SEND_BUF_SIZE];
    uint8_t                 rbuf[RECV_BUF_SIZE];
    uint8_t                 cmd_buf[MAX_CMD_LEN];
    cmd_map_t               *cmd_map;

    #if SUPPORT_CRYPT
    uint8_t                 crypt_key[48];
    uint8_t                 crypt_buf[MAX_CMD_LEN+16];
    int32_t                 (*encrypt)(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len, uint8_t *key);
    int32_t                 (*decrypt)(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len, uint8_t *key);
    #endif
} net_client_t;

typedef enum
{
    OTA_DEFAULT = 0x00,         //未升级
    OTA_UPGRAFE = 0x01,         //升级中
}OTA_STATE;

typedef enum
{
    OTA_ACCEPT = 0x00,          //接收
    OTA_REJECT = 0x01,          //拒绝
}OTA_RSP_STATE;

typedef enum
{
    OTA_SUCCESS = 0x00,         //成功
    OTA_FAILED = 0x01,          //失败
}OTA_DOWNLOAD_STATE;

typedef struct _ota_info
{
    OTA_STATE ota_state;                        //本地状态维护
    OTA_RSP_STATE rsp_state;                    //命令维护
    OTA_DOWNLOAD_STATE download_state;          //升级结果

    //文件基础信息
    uint8_t file_name[OTA_FILE_NAME_LEN];       //文件名字
    uint32_t file_total_len;                    //文件大小
    uint16_t file_crc;                          //文件crc

    //传输过程中变量
    uint16_t file_req_length;                   //文件请求大小
    uint32_t file_offset;                       //偏移
    uint16_t calculation_crc;                   //本地计算的crc

    //单包(同一包)请求次数,如果服务器下发错误包或者哪里出错,防止进入死循环
    uint8_t repeated_requests_num;              //次数为OTA_REPEATED_REQUESTS_NUM
} ota_info_t;

void encode_md5(unsigned char *out, int outlen, unsigned char *in, int in_len);
int aes_cbc128_encrypt(unsigned char *in, unsigned int inlen, unsigned char *out, unsigned int *outlen, unsigned char *aes_key);
int aes_cbc128_decrypt(unsigned char *in, unsigned int inlen, unsigned char *out, unsigned int *outlen, unsigned char *aes_key);

void net_client_init(net_client_t *client);
int32_t net_connect(net_client_t *client);
int32_t net_disconnect(net_client_t *client);

int32_t proto_encode_pack(net_client_t *client, uint8_t *data, uint16_t data_len, uint8_t cmd);
int32_t proto_try_enqueue_cmd(net_client_t *client, uint8_t cmd, void *param);
void proto_set_ack(uint16_t seq);

#ifdef __cplusplus
}
#endif

#endif
